package net.chrisrichardson.ormunit.hibernate;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

public class ClassPropertyUtil {

	public static Set<String> getPersistableFields(Class type, Class mappedSuper) {
		Set<String> persistableFields = new HashSet<String>();
		while (type != Object.class) {
			Field[] fields = type.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				Field field = fields[i];
				if (ClassPropertyUtil.isPersistableField(field)) {
					persistableFields.add(field.getName());
				}
			}
			type = type.getSuperclass();
			if (type.equals(mappedSuper))
				break;
		}
		return persistableFields;
	}

	public static boolean isPersistableField(Field field) {
		return (field.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) == 0;
	}

	public static Set<String> getPersistableProperties(Class type,
			Class mappedSuper) {
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(type);
			Set<String> result = new HashSet<String>();
			for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
				String name = pd.getName();
				if (!"class".equals(name)
						&& pd.getWriteMethod() != null
						&& pd.getReadMethod() != null
						&& isDefinedBySubclassOf(type, pd, mappedSuper))
					result.add(name);
			}
			return result;
		} catch (IntrospectionException e) {
			throw new RuntimeException(e);
		}
	}

	private static boolean isDefinedBySubclassOf(Class type,
			PropertyDescriptor pd, Class mappedSuper) {
		while (type != Object.class) {
			if (getDefinedByClass(pd) == type)
				return true;
			type = type.getSuperclass();
			if (type.equals(mappedSuper))
				break;
		}
		return false;
	}

	private static Class getDefinedByClass(PropertyDescriptor pd) {
		return pd.getReadMethod() != null ? pd.getReadMethod()
				.getDeclaringClass() : pd.getWriteMethod().getDeclaringClass();
	}

}
